feat: move collectData to per-PaymentOption and update IC flow#293
feat: move collectData to per-PaymentOption and update IC flow#293
Conversation
Move collectData from the top-level PaymentOptionsResponse to each individual PaymentOption, enabling different payment options to have different data collection requirements. - Add collectData field to Pay.PaymentOption and Wallet.Model.PaymentOption - Update mappers to propagate per-option collectData - Remove Intro screen (now handled by webview) - Add Summary screen (post-IC confirmation before payment) - Add "Why info required?" explanation dialog - Redesign options as flat card list with "Info required" badges - Button shows "Continue" for IC options, "Pay" for direct options - Bump yttrium-wcpay to 0.10.41 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Claude finished @jakubuid's task in 1m 35s —— View job Code Review CompletePR Size: 7 files, +501/-702 lines (net: -201 lines) ✅ Found 3 issue(s)Issue 1: Race condition in payment confirmation with collected dataID: paymentviewmodel-collectdata-race-a3f2 Context:
Recommendation: Clear fun onOptionSelected(optionId: String) {
val option = storedPaymentOptions.find { it.id == optionId } ?: return
selectedOptionId = optionId
// Clear stale collected data when changing options
if (option.collectData == null) {
collectedValues.clear()
}
val collectData = option.collectData
// ...rest of logic
}Issue 2: estimatedTxs coercion silently loses dataID: mappers-eta-truncation-b7d1 Context:
Recommendation: Since field name is estimatedTxs = option.etaS.let { eta ->
if (eta <= Int.MAX_VALUE.toULong()) eta.toInt() else null
}Issue 3: Unconstrained image URLs from payment responseID: paymentroute-asyncimage-xss-4c9a Context:
Recommendation: Validate icon URL schemes before loading: @Composable
private fun MerchantIcon(paymentInfo: Wallet.Model.PaymentInfo?, size: Dp) {
val iconUrl = paymentInfo?.merchant?.iconUrl?.takeIf { url ->
url.startsWith("https://", ignoreCase = true) || url.startsWith("http://", ignoreCase = true)
}
iconUrl?.let { validUrl ->
AsyncImage(model = validUrl, ...)
} ?: run {
// fallback placeholder
}
}SummaryArchitecture: Per-option State Management: ViewModel uses Critical Checks:
Manual Testing Required: |
...llet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/payment/PaymentRoute.kt
Show resolved
Hide resolved
…h navigation When navigating back and forth in the payment flow before completing a payment, the success modal would not dismiss on "Got it" or X click. Root causes: 1. Missing launchSingleTop on payment navigation caused duplicate dialog entries to stack on the backstack 2. popBackStack(Route.Connections.path) could silently fail if the backstack was in an unexpected state, leaving the dialog visible 3. SharedFlow replay=1 leaked stale payment data to duplicate ViewModel instances Fixes: WCP4-41 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ccess fix: payment success modal stuck after back/forth navigation
|
Claude finished @jakubuid's task in 2m 14s —— View job 🔄 Incremental Review: Previous Issues Status✅ Issue 1 (paymentviewmodel-collectdata-race-a3f2) - RESOLVED ✅ Issue 2 (mappers-eta-truncation-b7d1) - STILL EXISTS ✅ Issue 3 (paymentroute-asyncimage-xss-4c9a) - STILL EXISTS New IssuesNo new code quality or security issues found in this update. Breaking ChangesFound 2 breaking change(s)Issue 1: New parameter added to PaymentOption data class constructorID: brk-pay-paymentoption-collectdata-field-a8c3 Context:
Recommendation:
Issue 2: New parameter added to Wallet.Model.PaymentOption data class constructorID: brk-wallet-paymentoption-collectdata-field-b2f7 Context:
Recommendation:
Automated Checks✅ PR Size: 8 files, +522/-711 lines (under threshold) SummaryThis incremental review confirms that the previous race condition (Issue 1) has been resolved. The two remaining issues from the last review (ETA truncation and image URL validation) still exist but are low severity. Two new breaking changes were detected - both are low severity constructor signature changes with default values that maintain backward compatibility for typical SDK usage patterns. The migration path is straightforward: the top-level |
|


Summary
collectDatafrom top-levelPaymentOptionsResponseto each individualPaymentOption, enabling different payment options to have different data collection requirementsflowchart TD A[Payment Options] -->|Option has no collectData| B[Processing / Sign & Confirm] A -->|Option has collectData.url| C[WebView IC] A -->|Option has collectData.fields| D[Field-by-field IC] C --> E[Summary Screen] D --> E E -->|Confirm| B A -->|Why info required?| F[Explanation Dialog] F -->|Got it!| A B --> G[Success / Error]Model changes
Pay.PaymentOptionandWallet.Model.PaymentOption: addedcollectData: CollectDataAction? = nullcollectDatayttrium-wcpayto0.10.41for backend supportUI changes
IntroContent,IntroStepsTimeline,IntroStepItemcomposablesPayWithDropdown)PaymentOptionCard— flat cards with blue border selection + gray "Info required" badgeSummaryContent— post-IC confirmation with "Pay with" row and confirm buttonWhyInfoRequiredContent— regulatory compliance explanationMerchantIconandPaymentTitlehelper composablesTest plan
./gradlew :product:pay:testDebugUnitTest— passes./gradlew :product:walletkit:testDebugUnitTest— passes./gradlew :sample:wallet:assembleDebug— builds successfully🤖 Generated with Claude Code